#if defined(PLC_IS_APP) || defined(PLC)
/*
 * copyright 2008 Edouard TISSERANT
 * copyright 2011 Mario de Sousa (msousa@fe.up.pt)
 *
 * Offered to the public under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 * General Public License for more details.
 *
 * This code is made available on the understanding that it will not be
 * used in safety-critical situations without a full and competent review.
 */

/****
 * IEC 61131-3 standard function library
 */

/* NOTE: This file is full of (what may seem at first) very strange macros.
 *       If you want to know what all these strange macros are doing,
 *       just parse this file through a C preprocessor (e.g. cpp),
 *       and analyse the output!
 *       $gcc -E iec_std_lib.h
 */






#include "iec_types.h"
#include "iec_std_lib.h"

#ifdef DEBUG_IEC
#define DBG(...) printf(__VA_ARGS__);
#define DBG_TYPE(TYPENAME, name) __print_##TYPENAME(name);
#else
#define DBG(...)
#define DBG_TYPE(TYPENAME, name)
#endif

/*
 * Include type defs.
 */


/* TODO
typedef struct {
    __strlen_t len;
    u_int16_t body[STR_MAX_LEN];
} WSTRING;
*/
/*
# if __WORDSIZE == 64
#define __32b_sufix
#define __64b_sufix L
#else
#define __32b_sufix L
#define __64b_sufix LL
#endif
*/

# if __WORDSIZE == 64
#define __32b_sufix
#define __64b_sufix L
#else
#define __32b_sufix L
/* changed this from LL to L temporarily. It was causing a bug when compiling resulting code with gcc.
 * I have other things to worry about at the moment..
 */
#define __64b_sufix L
#endif


#define __lit(type, value, ...) (type)value##__VA_ARGS__
// Keep this macro expention step to let sfx(__VA_ARGS__) change into L or LL
#define __literal(type, value, ...) __lit(type,value,__VA_ARGS__)

#define __BOOL_LITERAL(value) __literal(BOOL,value)
#define __SINT_LITERAL(value) __literal(SINT,value)
#define __INT_LITERAL(value) __literal(INT,value)
#define __DINT_LITERAL(value) __literal(DINT,value,__32b_sufix)
#define __LINT_LITERAL(value) __literal(LINT,value,__64b_sufix)
#define __USINT_LITERAL(value) __literal(USINT,value)
#define __UINT_LITERAL(value) __literal(UINT,value)
#define __UDINT_LITERAL(value) __literal(UDINT,value,__32b_sufix)
#define __ULINT_LITERAL(value) __literal(ULINT,value,__64b_sufix)
#define __REAL_LITERAL(value) __literal(REAL,value,__32b_sufix)
#define __LREAL_LITERAL(value) __literal(LREAL,value,__64b_sufix)
#define __TIME_LITERAL(value) __literal(TIME,value)
#define __DATE_LITERAL(value) __literal(DATE,value)
#define __TOD_LITERAL(value) __literal(TOD,value)
#define __DT_LITERAL(value) __literal(DT,value)
#define __STRING_LITERAL(count, value) (STRING){count,value}
#define __BYTE_LITERAL(value) __literal(BYTE,value)
#define __WORD_LITERAL(value) __literal(WORD,value)
#define __DWORD_LITERAL(value) __literal(DWORD,value,__32b_sufix)
#define __LWORD_LITERAL(value) __literal(LWORD,value,__64b_sufix)



/**********************************************************************/
/**********************************************************************/
/*****                                                            *****/
/*****      Some helper functions...                              *****/
/*****                     ...used later:                         *****/
/*****    - when declaring the IEC 61131-3 standard functions     *****/
/*****    - in the C source code itself in SFC and ST expressions *****/
/*****                                                            *****/
/**********************************************************************/
/**********************************************************************/


/****************************/
/* Notify IEC runtime error */
/****************************/

/* function that generates an IEC runtime error */
void __iec_error(void) {
    /* TODO... */
//  fprintf(stderr, "IEC 61131-3 runtime error.\n");
    /*exit(1);*/
}



/**********************************************/
/* Time conversion to/from timespec functions */
/**********************************************/
/* NOTE: The following function was turned into a macro, so it could be used to initialize the initial value of TIME variables.
 *       Since each macro parameter is evaluated several times, the macro may result in multiple function invocations if an expression
 *       containing a function invocation is passed as a parameter. However, currently matiec only uses this conversion macro with
 *       constant literals, so it is safe to change it into a macro.
 */
/* NOTE: I (Mario - msousa@fe.up.pt) believe that the following function contains a bug when handling negative times.
 *       The equivalent macro has this bug fixed.
 *       e.g.;
 *          T#3.8s
 *       using the function, will result in a timespec of 3.8s !!!:
 *          tv_sec  =  4               <-----  1 *  3.8           is rounded up when converting a double to an int!
 *          tv_nsec = -200 000 000     <-----  1 * (3.8 - 4)*1e9
 *
 *         -T#3.8s
 *       using the function, will result in a timespec of -11.8s !!!:
 *          tv_sec  = -4                 <-----  -1 *  3.8 is rounded down when converting a double to an int!
 *          tv_nsec = -7 800 000 000     <-----  -1 * (3.8 - -4)*1e9
 */
/* NOTE: Due to the fact that the C compiler may round a tv_sec number away from zero,
 *       the following macro may result in a timespec that is not normalized, i.e. with a tv_sec > 0, and a tv_nsec < 0 !!!!
 *       This is due to the rounding that C compiler applies when converting a (long double) to a (long int).
 *       To produce normalized timespec's we need to use floor(), but we cannot call any library functions since we want this macro to be
 *       useable as a variable initializer.
 *       VAR x : TIME = T#3.5h; END_VAR --->  IEC_TIME x = __time_to_timespec(1, 0, 0, 0, 3.5, 0);
 */
/*
IEC_TIMESPEC __time_to_timespec(int sign, double mseconds, double seconds, double minutes, double hours, double days) {
  IEC_TIMESPEC ts;

  // sign is 1 for positive values, -1 for negative time...
  long double total_sec = ((days*24 + hours)*60 + minutes)*60 + seconds + mseconds/1e3;
  if (sign >= 0) sign = 1; else sign = -1;
  ts.tv_sec = sign * (long int)total_sec;
  ts.tv_nsec = sign * (long int)((total_sec - ts.tv_sec)*1e9);

  return ts;
}
*/
/* NOTE: Unfortunately older versions of ANSI C (e.g. C99) do not allow explicit identification of elements in initializers
 *         e.g.  {tv_sec = 1, tv_nsec = 300}
 *       They are therefore commented out. This however means that any change to the definition of IEC_TIMESPEC may require this
 *       macro to be updated too!
 */
#define __time_to_timespec(sign, mseconds, seconds, minutes, hours, days) \
          ((IEC_TIMESPEC){\
              /*tv_sec  =*/ ((long int)   (((sign>=0)?1:-1)*((((long double)days*24 + (long double)hours)*60 + (long double)minutes)*60 + (long double)seconds + (long double)mseconds/1e3))), \
              /*tv_nsec =*/ ((long int)(( \
                            ((long double)(((sign>=0)?1:-1)*((((long double)days*24 + (long double)hours)*60 + (long double)minutes)*60 + (long double)seconds + (long double)mseconds/1e3))) - \
                            ((long int)   (((sign>=0)?1:-1)*((((long double)days*24 + (long double)hours)*60 + (long double)minutes)*60 + (long double)seconds + (long double)mseconds/1e3)))   \
                            )*1e9))\
        })




/* NOTE: The following function was turned into a macro, so it could be used to initialize the initial value of TOD (TIME_OF_DAY) variables */
/* NOTE: many (but not all) of the same comments made regarding __time_to_timespec() are also valid here, so go and read those comments too!*/
/*
IEC_TIMESPEC __tod_to_timespec(double seconds, double minutes, double hours) {
  IEC_TIMESPEC ts;

  long double total_sec = (hours*60 + minutes)*60 + seconds;
  ts.tv_sec = (long int)total_sec;
  ts.tv_nsec = (long int)((total_sec - ts.tv_sec)*1e9);

  return ts;
}
*/
#define __tod_to_timespec(seconds, minutes, hours) \
          ((IEC_TIMESPEC){\
              /*tv_sec  =*/ ((long int)   ((((long double)hours)*60 + (long double)minutes)*60 + (long double)seconds)), \
              /*tv_nsec =*/ ((long int)(( \
                            ((long double)((((long double)hours)*60 + (long double)minutes)*60 + (long double)seconds)) - \
                            ((long int)   ((((long double)hours)*60 + (long double)minutes)*60 + (long double)seconds))   \
                            )*1e9))\
        })


/*******************/
/* Time operations */
/*******************/

#define __time_cmp(t1, t2) (t2.tv_sec == t1.tv_sec ? t1.tv_nsec - t2.tv_nsec : t1.tv_sec - t2.tv_sec)

IEC_TIME iec_time_mul(IEC_TIME IN1, IEC_LREAL IN2);

TIME __time_mul(TIME IN1, LREAL IN2) {
    return iec_time_mul(IN1, IN2);
}

IEC_TIME iec_time_div(IEC_TIME IN1, IEC_LREAL IN2);

TIME __time_div(TIME IN1, LREAL IN2) {
    return iec_time_div(IN1, IN2);
}


/***************/
/* Convertions */
/***************/
/*****************/
/*  REAL_TO_INT  */
/*****************/
double fmod(double x, double y);

LINT __real_round(LREAL IN) {
    return fmod(IN, 1) == 0 ? ((LINT) IN / 2) * 2 : (LINT) IN;
}

IEC_STRING iec_bit_to_string(IEC_LWORD IN);

STRING __bit_to_string(LWORD IN) {
    return iec_bit_to_string(IN);
}

IEC_STRING iec_real_to_string(IEC_LREAL IN);

STRING __real_to_string(LREAL IN) {
    return iec_real_to_string(IN);
}

IEC_STRING iec_sint_to_string(IEC_LINT IN);

STRING __sint_to_string(LINT IN) {
    return iec_sint_to_string(IN);
}

IEC_STRING iec_uint_to_string(IEC_ULINT IN);

STRING __uint_to_string(ULINT IN) {
    return iec_uint_to_string(IN);
}
/***************/
/* FROM_STRING */
/***************/
IEC_BOOL iec_string_to_bool(IEC_STRING IN);

BOOL __string_to_bool(STRING IN) {
    return iec_string_to_bool(IN);
}

IEC_LREAL iec_string_to_real(IEC_STRING IN);

LREAL __string_to_real(STRING IN) {
    return iec_string_to_real(IN);
}

/***************/
/*   TO_TIME   */
/***************/

TIME __real_to_time(LREAL IN) { return (TIME) {IN, (IN - (LINT) IN) * 1000000000}; }

IEC_TIME iec_string_to_time(IEC_STRING IN);

TIME __string_to_time(STRING IN) {
    return iec_string_to_time(IN);
}

/***************/
/*  FROM_TIME  */
/***************/
IEC_STRING iec_time_to_string(IEC_TIME IN);

STRING __time_to_string(TIME IN) {
    return iec_time_to_string(IN);
}

IEC_STRING iec_date_to_string(IEC_DATE IN);

STRING __date_to_string(DATE IN) {
    return iec_date_to_string(IN);
}

IEC_STRING iec_tod_to_string(IEC_TOD IN);

STRING __tod_to_string(TOD IN) {
    return iec_tod_to_string(IN);
}

IEC_STRING iec_dt_to_string(IEC_DT IN);

STRING __dt_to_string(DT IN) {
    return iec_dt_to_string(IN);
}

/**********************************************/
/*  [ANY_DATE | TIME] _TO_ [ANY_DATE | TIME]  */
/**********************************************/

TOD __date_and_time_to_time_of_day(DT IN) {
    return (TOD) {
            IN.tv_sec % SECONDS_PER_DAY + (IN.tv_sec < 0 ? SECONDS_PER_DAY : 0),
            IN.tv_nsec};
}

DATE __date_and_time_to_date(DT IN) {
    return (DATE) {
            IN.tv_sec - IN.tv_sec % SECONDS_PER_DAY - (IN.tv_sec < 0 ? SECONDS_PER_DAY : 0),
            0};
}

/************/
/*  MOVE_*  */
/************/

/* some helpful __move_[ANY] functions, used in the *_TO_** and MOVE  standard functions */
/* e.g. __move_BOOL, __move_BYTE, __move_REAL, __move_TIME, ... */
#define __move_(TYPENAME)\
TYPENAME __move_##TYPENAME(TYPENAME op1) {return op1;}

#endif
